/*
 * Tencent is pleased to support the open source community by making
 * MMKV available.
 *
 * Copyright (C) 2018 THL A29 Limited, a Tencent company.
 * All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *       https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tencent.mmkv;


import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.aafwk.ability.LocalRemoteObject;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.app.Context;
import ohos.data.preferences.Preferences;
import ohos.event.commonevent.CommonEventData;
import ohos.event.commonevent.CommonEventManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.rpc.IRemoteObject;
import ohos.rpc.RemoteException;

import java.util.Random;

import static com.tencent.mmkv.Util.LABEL;

public abstract class BenchMarkBaseService extends Ability {
    public static final String CMD_ID = "cmd_id";
    public static final String CMD_READ_INT = "cmd_read_int";
    public static final String CMD_WRITE_INT = "cmd_write_int";
    public static final String CMD_READ_STRING = "cmd_read_string";
    public static final String CMD_WRITE_STRING = "cmd_write_string";
    public static final String CMD_PREPARE_ASHMEM_BY_CP = "cmd_prepare_ashmem_by_ContentProvider";
    public static final String CMD_PREPARE_ASHMEM_KEY = "cmd_prepare_ashmem_key";

    // 1M, ashmem cannot change size after opened
    public static final int AshmemMMKV_Size = 1024 * 1024;
    public static final String AshmemMMKV_ID = "testAshmemMMKVByCP";

    private String[] m_arrStrings;
    private String[] m_arrKeys;
    private String[] m_arrIntKeys;

    private static final int m_loops = 1000;
    public static final String MMKV_ID = "benchmark_interprocess";
    //public static final String MMKV_ID = "benchmark_interprocess_crypt1";
    private static final String SP_ID = "benchmark_interprocess_sp";
    public static final String CryptKey = null;
    //public static final String CryptKey = "Tencent MMKV";
    private static final boolean SQLite_Use_Transaction = false;

//    private final int My_Log = 0;
    public HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x78526, "MMKV");



    private MyRemote remote = new MyRemote("MyRemoteAbility");

    class MyRemote extends MyIdlInterfaceStub {
        private String MyMsg;

        public String getMyMsg() {
            return MyMsg;
        }

        public void setMyMsg(String myMsg) {
            MyMsg = myMsg;
        }

        public MyRemote(String descriptor) {
            super(descriptor);
        }


        @Override
        public ParcelableMMKV getAshmemMMKV() throws RemoteException {
            return null;
        }

        @Override
        public void getMsg(String msg) throws RemoteException {

        }


        @Override
        public void serviceMethod1(int anInt) throws RemoteException {

        }
    }


    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        HiLog.info(label, "onCreate BenchMarkBaseService");

        MMKV.initialize((Context) this);
        MMKV.unregisterHandler();
        MMKV.unregisterContentChangeNotify();
        {
            long startTime = System.currentTimeMillis();

            MMKV mmkv = MMKV.mmkvWithID(MMKV_ID, MMKV.MULTI_PROCESS_MODE, CryptKey);

            long endTime = System.currentTimeMillis();
            HiLog.info(label, "load [" + MMKV_ID + "]: " + (endTime - startTime) + " ms");
        }
        m_arrStrings = new String[m_loops];
        m_arrKeys = new String[m_loops];
        m_arrIntKeys = new String[m_loops];
        Random r = new Random();
        final String filename =
                "OHOS_APP_MMKV/entry/src/main/java/com/tencent/mmkvdemo/BenchMarkBaseService.java_";
        for (int index = 0; index < m_loops; index++) {
            //String str = "[MMKV] [Info]<MemoryFile_OSX.cpp:36>: protection on [/var/mobile/Containers/Data/Application/B93F2BD3-E0DB-49B3-9BB0-C662E2FC11D9/Documents/mmkv/cips_commoncache] is NSFileProtectionCompleteUntilFirstUserAuthentication_";
            //m_arrStrings[index] = str + r.nextInt();
            m_arrStrings[index] = filename + r.nextInt();
            m_arrKeys[index] = "testStr_" + index;
            m_arrIntKeys[index] = "int_" + index;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        HiLog.info(label, "onDestroy BenchMarkBaseService");
        MMKV.onExit();
    }

    protected void batchWriteInt(String caller) {
        mmkvBatchWriteInt(caller);
        sqliteWriteInt(caller, SQLite_Use_Transaction);
        spBatchWriteInt(caller);
    }

    private void mmkvBatchWriteInt(String caller) {
        Random r = new Random();
        long startTime = System.currentTimeMillis();

        MMKV mmkv = GetMMKV();
        for (int index = 0; index < m_loops; index++) {
            int tmp = r.nextInt();
            String key = m_arrIntKeys[index];
            mmkv.encode(key, tmp);
        }
        long endTime = System.currentTimeMillis();
        String WriteInt = caller + " mmkv write int: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(WriteInt);
        HiLog.info(label, WriteInt);
    }

    private void sqliteWriteInt(String caller, boolean useTransaction) {
        Random r = new Random();
        long startTime = System.currentTimeMillis();

        SQLIteKV sqlIteKV = new SQLIteKV((Context) this);
        if (useTransaction) {
            sqlIteKV.beginTransaction();
        }
        for (int index = 0; index < m_loops; index++) {
            int tmp = r.nextInt();
            String key = m_arrIntKeys[index];
            sqlIteKV.putInt(key, tmp);
        }
        if (useTransaction) {
            sqlIteKV.endTransaction();
        }
        long endTime = System.currentTimeMillis();
        String msg = caller + (useTransaction ? " sqlite transaction " : " sqlite ") + "write int: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(msg);
        HiLog.info(label, msg);
    }

    private void spBatchWriteInt(String caller) {
        Random r = new Random();
        long startTime = System.currentTimeMillis();

        Preferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);
        for (int index = 0; index < m_loops; index++) {
            int tmp = r.nextInt();
            String key = m_arrIntKeys[index];
            preferences.putInt(key, tmp);
            //editor.commit();
            preferences.flush();
        }
        long endTime = System.currentTimeMillis();
        String spWriteInt = caller + " MultiProcessSharedPreferences write int: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(spWriteInt);
        HiLog.info(label, spWriteInt);
    }

    protected void batchReadInt(String caller) {
        mmkvBatchReadInt(caller);
        sqliteReadInt(caller, SQLite_Use_Transaction);
        spBatchReadInt(caller);
    }

    private void mmkvBatchReadInt(String caller) {
        long startTime = System.currentTimeMillis();

        MMKV mmkv = GetMMKV();
        for (int index = 0; index < m_loops; index++) {
            String key = m_arrIntKeys[index];
            int tmp = mmkv.decodeInt(key);
        }
        long endTime = System.currentTimeMillis();
        String mmkvReadInt = caller + " mmkv read int: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(mmkvReadInt);
        HiLog.info(label, mmkvReadInt);
    }

    private void sqliteReadInt(String caller, boolean useTransaction) {
        long startTime = System.currentTimeMillis();

        SQLIteKV sqlIteKV = new SQLIteKV((Context) this);
        if (useTransaction) {
            sqlIteKV.beginTransaction();
        }
        for (int index = 0; index < m_loops; index++) {
            String key = m_arrIntKeys[index];
            int tmp = sqlIteKV.getInt(key);
        }
        if (useTransaction) {
            sqlIteKV.endTransaction();
        }
        long endTime = System.currentTimeMillis();
        final String msg = caller + (useTransaction ? " sqlite transaction " : " sqlite ") + "read int: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(msg);
        HiLog.info(label, msg);
    }

    private void spBatchReadInt(String caller) {
//        Util.log("spBatchReadInt---");
        long startTime = System.currentTimeMillis();
        Preferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);
        for (int index = 0; index < m_loops; index++) {
            String key = m_arrIntKeys[index];
//            Util.log("spBatchReadInt  ----key=%s",key);
            int tmp = preferences.getInt(key, 0);
        }
        long endTime = System.currentTimeMillis();
        String apreadInt = caller + " MultiProcessSharedPreferences read int: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(apreadInt);
        HiLog.info(label, apreadInt);
    }

    protected void batchWriteString(String caller) throws RemoteException {
        mmkvBatchWriteString(caller);
        sqliteWriteString(caller, SQLite_Use_Transaction);
        spBatchWrieString(caller);
    }

    private void mmkvBatchWriteString(String caller) throws RemoteException {
        long startTime = System.currentTimeMillis();

        MMKV mmkv = GetMMKV();
        for (int index = 0; index < m_loops; index++) {
            final String valueStr = m_arrStrings[index];
            final String strKey = m_arrKeys[index];
            mmkv.encode(strKey, valueStr);
        }
        long endTime = System.currentTimeMillis();
        String mmkvws = caller + " mmkv write String: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(mmkvws);
        HiLog.info(label, mmkvws);
    }

    private void sqliteWriteString(String caller, boolean useTransaction) throws RemoteException {
        long startTime = System.currentTimeMillis();

        SQLIteKV sqlIteKV = new SQLIteKV((Context) this);
        if (useTransaction) {
            sqlIteKV.beginTransaction();
        }
        for (int index = 0; index < m_loops; index++) {
            final String value = m_arrStrings[index];
            final String key = m_arrKeys[index];
            sqlIteKV.putString(key, value);
        }
        if (useTransaction) {
            sqlIteKV.endTransaction();
        }
        long endTime = System.currentTimeMillis();
        String msg = caller + (useTransaction ? " sqlite transaction " : " sqlite ") + "write String: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";

        send(msg);
        HiLog.info(label, msg);
    }

    private void spBatchWrieString(String caller) throws RemoteException {
        long startTime = System.currentTimeMillis();

        Preferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);
        for (int index = 0; index < m_loops; index++) {
            final String str = m_arrStrings[index];
            final String key = m_arrKeys[index];
            preferences.putString(key, str);
            //editor.commit();
            preferences.flush();
        }
        long endTime = System.currentTimeMillis();
        String spws = caller + " MultiProcessSharedPreferences write String: loop[" + m_loops
                + "]: " + (endTime - startTime) + " ms";

        send(spws);
        HiLog.info(label, spws);
    }

    protected void batchReadString(String caller) {
        mmkvBatchReadString(caller);
        sqliteReadString(caller, SQLite_Use_Transaction);
        spBatchReadStrinfg(caller);
    }

    private void mmkvBatchReadString(String caller) {
        long startTime = System.currentTimeMillis();

        MMKV mmkv = GetMMKV();
        for (int index = 0; index < m_loops; index++) {
            final String strKey = m_arrKeys[index];
            final String tmpStr = mmkv.decodeString(strKey);
        }
        long endTime = System.currentTimeMillis();
        String mmkvrs = caller + " mmkv read String: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(mmkvrs);
        HiLog.info(label, mmkvrs);
    }

    private void sqliteReadString(String caller, boolean useTransaction) {
        long startTime = System.currentTimeMillis();

        SQLIteKV sqlIteKV = new SQLIteKV((Context) this);
        if (useTransaction) {
            sqlIteKV.beginTransaction();
        }
        for (int index = 0; index < m_loops; index++) {
            final String key = m_arrKeys[index];
            final String tmp = sqlIteKV.getString(key);
        }
        if (useTransaction) {
            sqlIteKV.endTransaction();
        }
        long endTime = System.currentTimeMillis();
        final String msg = caller + (useTransaction ? " sqlite transaction " : " sqlite ") + "read String: loop[" + m_loops + "]: " + (endTime - startTime) + " ms";
        send(msg);
        HiLog.info(label, msg);
    }

    private void spBatchReadStrinfg(String caller) {
        long startTime = System.currentTimeMillis();
        Preferences preferences = MultiProcessSharedPreferences.getSharedPreferences(this, SP_ID, MODE_PRIVATE);
        for (int index = 0; index < m_loops; index++) {
            final String key = m_arrKeys[index];
            final String tmp = preferences.getString(key, null);
        }
        long endTime = System.currentTimeMillis();
        String sprs = caller + " MultiProcessSharedPreferences read String: loop[" + m_loops
                + "]: " + (endTime - startTime) + " ms";
        send(sprs);
        HiLog.info(label, sprs);
    }

    MMKV m_ashmemMMKV;

    protected MMKV GetMMKV() {
        if (m_ashmemMMKV != null) {
            return m_ashmemMMKV;
        } else {
            return MMKV.mmkvWithID(MMKV_ID, MMKV.MULTI_PROCESS_MODE, CryptKey);
        }
    }

    public void send(String serviceMsg) {
        try {
            Intent intent = new Intent();
            Operation operation = new Intent.OperationBuilder()
                    .withAction("com.my.test")
                    .build();
            intent.setOperation(operation);
            intent.setParam("msg", serviceMsg);
            CommonEventData eventData = new CommonEventData(intent);
            CommonEventManager.publishCommonEvent(eventData);
        } catch (RemoteException e) {
            HiLog.error(LABEL, "Exception occurred during publishCommonEvent invocation.");
        }

    }


    // 把IRemoteObject返回给客户端
    @Override
    protected IRemoteObject onConnect(Intent intent) {
        return remote.asObject();
    }

    protected void prepareAshmemMMKVByCP() {
        // it's ok for other process not knowing cryptKey
        final String cryptKey = null;
        m_ashmemMMKV = MMKV.mmkvWithAshmemID((Context) this, AshmemMMKV_ID, AshmemMMKV_Size, MMKV.MULTI_PROCESS_MODE, cryptKey);

    }
}
